一 前言
文章介绍了一个现代化的项目的webpack4环境是什么样的。这里只是介绍了基础的功能,如果需要详细的相关只是可以去webpack官网去查阅。
代码地址:github
环境特点:
1.使用了webpack-dev-middleware,在文件内容更改之后自动编译;
2.使用了webpack-hot-middleware,在热编译之后会自动刷新页面更改的内容,而不是刷新整个页面
3.使用了server.js文件来自己控制启动http服务,后期可以扩展简单的后端功能
文章将webpack的配置文件写成了三份:公用部分文件、开发环境文件、线上环境文件,具体在文章中会有详细的介绍。
注意:文章介绍很详细,适合新手入门使用,请耐心阅读。
二 正文
1.path相关内容
文档:http://nodejs.cn/api/path.html
我们经常用到的就是path.resolve和path.join,那么我们就讲一下两者的用法和区别:
相关文章:Difference between path.resolve and path.join invocation?
(1)path.join
path.join() 方法使用平台特定的分隔符把全部给定的 path 片段连接到一起,并规范化生成的路径。
path.join('/a', '/b') // Outputs '/a/b'
path.join('/foo', 'bar', 'baz/asdf', 'quux', '..'); // outputs '/foo/bar/baz/asdf'
summary:path.join通常用到的是简单的将字符串进行拼接。
(2)path.resolve
path.resolve() 方法会把一个路径或路径片段的序列解析为一个绝对路径。
给定的路径的序列是从右往左被处理的,后面每个 path 被依次解析,直到构造完成一个绝对路径(就会停止解析)。
例如,给定的路径片段的序列为:/foo、/bar、baz,则调用 path.resolve('/foo', '/bar', 'baz') 会返回 /bar/baz。
如果处理完全部给定的 path 片段后还未生成一个绝对路径,则当前工作目录会被用上。
生成的路径是规范化后的,且末尾的斜杠会被删除,除非路径被解析为根目录。
长度为零的 path 片段会被忽略。
如果没有传入 path 片段,则 path.resolve() 会返回当前工作目录的绝对路径。
例子:
path.resolve('/foo/bar', './baz');
// 返回: '/foo/bar/baz'
path.resolve('/foo/bar', '/tmp/file/');
// 返回: '/tmp/file
(3)work directory 与 dirname
工作目录和文件目录并不是一直相等,我们以'./src/view/index.js'文件为例:
文件目录是固定的,就是文件所在的目录:./src/view/index.js
工作目录是不确定的,查看当前所在目录:pwd
如果你是在./src/view/ 下执行的index.js,那么就和文件路径相同./src/view/index.js
但是如果你在./src下执行的index.js,那工作路径则是./src
2.从server.js谈起
我们要知道server.js都做了些什么:
1.开启一个http服务器
2.使用热编译中间件,实时编译修改过的内容
3.使用热替换,实时查看最新的页面UI
4.【扩展】可以做一些后端的东西
代码如下:
const http = require('http');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
// 使用express启用一个服务器
const express = require('express');
// 引用开发环境下的webpack配置文件
const config = require('./webpack.dev');
const app = express();
const webpackConfig = webpack(config);
const devMiddlewareCompiler = webpackDevMiddleware(webpackConfig,{
publicPath:config.output.publicPath
});
const hotMiddlewareCompiler = webpackHotMiddleware(webpackConfig,{
log: false,
heartbeat: 2000,
})
app.use(devMiddlewareCompiler);// 使用热编译中间件
app.use(hotMiddlewareCompiler);// 使用热替换中间件
app.listen(8080,function(){
console.log('Example app listening on port 8080!\n');
});
注意:为什么要把热编译的功能放在node里面呢?如果引用wepack-dev-server会自动管理热编译,它的原理也还是利用express开启了一个小型服务器,只不过我们看不到它。所以如果你要自己控制,并且想简单方便的在后端做点小东西,可以完全使用上面的方法。如果是后端比较重就不建议这么写了,你需要开启两台服务,通过代理的方式模拟进行前后端通信了。
3.webpack common
webpack.common,js配置了无论是开发还是发布都需要的东西,比如一些loader的转译,代码的打包压缩等。
具体代码如下:
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js' // 入口文件index.js
},
module:{
rules:[{
test:/\.css$/,
use:[
'style-loader',
'css-loader'
],
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
],
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'Production'
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
}
};
下面讲一讲主要做了什么事:
3-1 file-loader
a. 加载图片
如果我们要想像引用模块那样引用一个图片,例如:
import Rose from './img/rose.jpg'
在 webpack 里负责图片翻译的是 file-loader。而且,webpack 在最终构建时,会自动将模块中引用的图片拷贝到相应目录。如果你检查此元素,你将看到实际的文件名已更改为像 5c999da72346a995e7e2718865d019c8.png 一样。这意味着 webpack 在 src 文件夹中找到我们的文件,并成功处理过它!
b. 加载字体
css文件中引用字体:
@font-face {
font-family: 'MyFont';
src: url('./my-font.woff2') format('woff2'),
url('./my-font.woff') format('woff');
font-weight: 600;
font-style: normal;
}
【扩展】file-loader与url-loader
如果我们希望在页面引入图片(包括img的src和background的url)。当我们基于webpack进行开发时,引入图片会遇到一些问题。其中一个就是引用路径的问题。拿background样式用url引入背景图来说,我们都知道,webpack最终会将各个模块打包成一个文件,因此我们样式中的url路径是相对入口html页面的,而不是相对于原始css文件所在的路径的。这就会导致图片引入失败。这个问题是用file-loader解决的,file-loader可以解析项目中的url引入(不仅限于css),根据我们的配置,将图片拷贝到相应的路径,再根据我们的配置,修改打包后文件引用路径,使之指向正确的文件。另外,如果图片较多,会发很多http请求,会降低页面性能。这个问题可以通过url-loader解决。url-loader会将引入的图片编码,生成dataURl。相当于把图片数据翻译成一串字符。再把这串字符打包到文件中,最终只需要引入这个文件就能访问图片了。当然,如果图片较大,编码会消耗性能。因此url-loader提供了一个limit参数,小于limit字节的文件会被转为DataURl,大于limit的还会使用file-loader进行copy。
//url-loader封装了file-loader。url-loader不依赖于file-loader,即使用url-loader时,只需要安装url-loader即可
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
}
]
}
}
3-2 css loader与style-loader
如果你在JS中使用:
import './index.css'
我们需要 CSS 加载器:
(1) css-loader - 预处理 CSS 文件
(2) style-loader - 将 CSS 插入到 DOM 中的 style 标签
要查看 webpack 做了什么,请检查页面(不要查看页面源代码,因为它不会显示结果),并查看页面的 head 标签。它应该包含我们在 index.js 中导入的 style 块元素:<style>内容</style>。
注意:
(1)加载器的顺序是从后往前的,loader 的顺序很重要:如果把 style-loader 放到 css-loader 后面,我们就会撞见错误。
(2)我们如果只使用了 css-loader,则 webpack 只是将 CSS 文件预处理成模块然后打包到构建文件中,并不会插入到页面。
【扩展】将CSS单独打包:
webpack1/2/3:extract-text-webpack-plugin
webpack4:mini-css-extract-plugin
3-3 htm-lwebpack-plugin
详细介绍见:html-webpack-plugin npm
这是一个webpack插件,可以简化创建HTML文件以便为webpack包提供服务。 这对于webpack包来说特别有用,它在文件名中包含一个hash,用于更改每个编译。 你可以让插件为你生成一个HTML文件,使用lodash模板提供你自己的模板或使用你自己的加载器。
3-4 clean-webpack-plugin
详细介绍见:clean-wenpack-plugin
一个在创建之前清除你build文件夹的webpack插件。在打包生成新的build文件的时候清除之前生成的,这非常有用。
4.webpack development
两种环境的配置在webpack4中都支持mode的配置:development/production,具体的默认配置查询可以移步这里:webpack4 Mode的默认设置 。
webpack.dev.config.js:
const webpack = require('webpack');
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode:'development',
plugins:[
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
]
});
这里做了两件事:
(1)mode:'development':定义环境为开发环境。在webpack4之后省了很多操作,只需要指定为开发环境,就会自动设定source map等信息
(2)HotModuleReplacementPlugin:模块热替换
使用热替换需要两步:
首先: Add the following plugins to the plugins array
plugins: [
// OccurenceOrderPlugin is needed for webpack 1.x only
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
// Use NoErrorsPlugin for webpack 1.x
new webpack.NoEmitOnErrorsPlugin()
]
Occurence ensures consistent build hashes, hot module replacement is somewhat self-explanatory, no errors is used to handle errors more cleanly.
其次:Add 'webpack-hot-middleware/client' into the entry array.
This connects to the server to receive notifications when the bundle rebuilds and then updates your client bundle accordingly.
5.webpack production
两种环境的配置在webpack4中都支持mode的配置:development/production,具体的默认配置查询可以移步这里:webpack4 Mode的默认设置 。
const webpack = require('webpack');
const path = require('path');
const merge = require('webpack-merge');
// const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const common = require('./webpack.common.js');
module.exports = merge(common, {
output:{
// publicPath:path.resolve(__dirname, 'dist'),
},
mode:'production',
});
指定环境为生产环境,默认开启UglifyJSPlugin。
【扩展】将文件标记为无副作用(side-effect-free)
src/math.js:
export function square(x) {
return x * x;
}
export function cube(x) {
return x * x * x;
}
src/index.js:
import { cube } from './math.js';
math.js文件的square函数没有被导入,但是,它仍然被包含在 bundle 中。
在一个纯粹的 ESM 模块世界中,识别出哪些文件有副作用很简单。然而,我们的项目无法达到这种纯度,所以,此时有必要向 webpack 的 compiler 提供提示哪些代码是“纯粹部分”。
这种方式是通过 package.json 的 "sideEffects" 属性来实现的。
{
"name": "your-project",
"sideEffects": false
}
如同上面提到的,如果所有代码都不包含副作用,我们就可以简单地将该属性标记为 false,来告知 webpack,它可以安全地删除未用到的 export 导出。
如果你的代码确实有一些副作用,那么可以改为提供一个数组:
{
"name": "your-project",
"sideEffects": [
"./src/some-side-effectful-file.js"
]
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。